package BloomfilterSelf;

import java.util.BitSet;
import java.util.Random;

public class BloomFilter_Self {

    private BitSet bitset;  // 位数组
    private int bitSetSize;  // 位数组大小 长度
    private int expectedNumberOfElements;  // 期望元素数量  也可以是 预期误差
    private int numHashFunctions;  // 哈希函数数量

    /**
     *  用于生成哈希种子的伪随机数生成器
     */
    private Random random = new Random();

    public BloomFilter_Self(int bitSetSize, int expectedNumberOfElements) {
        this.bitSetSize = bitSetSize;
        this.expectedNumberOfElements = expectedNumberOfElements;

        // 通过 传进来得数据计算出 哈希函数数量
        this.numHashFunctions = (int) Math.round((bitSetSize / expectedNumberOfElements) * Math.log(2.0));

        // 初始化位数组
        this.bitset = new BitSet(bitSetSize);
    }

    // 把元素经过n个哈希函数计算后的哈希值，获取对应索引，放进去
    public void add(Object element){
        for (int i = 0; i < numHashFunctions; i++) {
            // 计算哈希值
            long hash = computeHash(element.toString(), i);
            int index = calculateIndex(hash);  // 拿到对应位数组的索引
            bitset.set(index, true);
        }
    }

    // 判断元素是否在集合中
    public boolean isContains( Object element) {
        // 1. 计算哈希值  2. 计算对应索引  3. 判断是否为true 就是是否为1
        for (int i = 0; i < numHashFunctions; i++) {
            long hash = computeHash(element.toString(), i);
            int index = calculateIndex(hash);
            boolean result = bitset.get(index);
            if (result ==false ) {
                return false;
            }
            // 由于循环完了才会是 return true
            // 所以 每个hash值对应的位数组的值为 true 才说明元素存在
        }
        return true;
    }




    // 计算hash值对应位数组的索引
    public int calculateIndex( long hash ){
        // 保证返回的是正数
        return (int) Math.abs(hash%bitSetSize);

    }

    /**
     * 计算元素哈希值
     * @param element
     * @param seed
     * @return
     */
    private long computeHash(String element, int seed) {
        // 使用伪随机数生成器生成不同的哈希种子
        random.setSeed(seed);

        // 将元素转换为字节数组，并计算其哈希值
        byte[] data = element.getBytes();
        long hash = 0x7f52bed27117b5efL;

        for (byte b : data) {
            hash ^= random.nextInt();
            hash *= 0xcbf29ce484222325L;
            hash ^= b;
        }

        return hash;
    }
}
